一个有意思的实验#
在深入学习 Claude Code 的过程中,我发现了一个很有意思的现象。同样是"修复登录功能的 bug"这个需求,有时候 AI 会给出很精准的解决方案,有时候却只能给出一些通用的、无法直接应用的建议。
后来我做了个实验。第一次,我只说"修复登录功能的 bug",Claude Code 给了我一些通用的排查建议,比如检查数据库连接、验证 JWT 配置等,但都不太适用。第二次,我换了个说法:"修复登录功能的 bug,项目使用 Next.js + Prisma + JWT,错误信息是 TypeError: Cannot read 'id' of undefined",这次 AI 直接定位到了问题所在,并给出了准确的修复方案。
两次尝试的差异说明:AI 的输出质量,很大程度上取决于我们提供的信息,也就是所谓的"上下文"(Context)。对于大型语言模型来说,上下文就像是它的"视野范围"和"知识来源"。你提供什么样的信息,它就基于什么样的信息来生成回答。
这篇文章,我会从 LLM 的工作机制讲起,系统地分析如何优化上下文的五个关键维度,帮助你在使用 Claude Code 时获得更好的输出质量。
LLM 的工作原理:为什么上下文是关键#
反直觉的事实:LLM 并不"理解"你的问题#
传统程序:
if (condition) {
return action; // 基于规则执行
}
LLM 的工作方式:
扫描上下文 → 计算概率分布 → 选择最可能的下一个词核心机制:
- LLM 基于统计概率预测下一个最可能的词
- 它的"理解"来自于上下文中的信息模式
- 上下文越相关、越丰富,预测越准确
上下文窗口:LLM 的"视野范围"#
┌─────────────────────────────────────┐
│ 上下文窗口(200K tokens) │
├─────────────────────────────────────┤
│ [系统提示词] │
│ [对话历史] │
│ [用户当前输入] │
│ [工具调用结果] │
│ [文件内容] │
└─────────────────────────────────────┘
↓
这是 LLM 能"看到"的全部信息
↓
基于这些信息生成回复技术规格:
- Claude 3.5 Sonnet:200K tokens(约 40 万中文字符)
- GPT-4 Turbo:128K tokens
- 上下文窗口外的信息会被截断或遗忘
限制的影响:
- 信息超出窗口 → LLM 无法访问
- 上下文质量差 → 输出质量差
- 无关信息多 → 干扰判断
上下文工程的定义和目标#
定义:
上下文工程(Context Engineering):
在有限的 Token 窗口内,优化提供给 LLM 的信息,
以最大化输出质量和准确性的技术和方法论核心目标:
- 提供足够的信息(让 LLM 理解任务)
- 保持精简(不超出 Token 限制)
- 确保相关(排除无关信息)
- 结构清晰(便于 LLM 理解)
上下文的本质:LLM 的"记忆"机制#
LLM 的"记忆"是什么?#
LLM 并没有真正意义上的"记忆"。它不像人类那样有长期记忆和短期记忆的区分,也不会"记住"之前的对话。
LLM 的"记忆"实际上是:
人类的记忆:
经验 → 存储在大脑 → 需要时回忆 → 提取信息
LLM 的"记忆":
训练数据 → 固化为模型参数 → 推理时 → 基于上下文窗口内的信息关键差异:
训练知识(固定):LLM 训练时学到的知识,存储在模型参数中
- 示例:知道 React 是什么、JavaScript 的语法
- 特点:不会改变(除非重新训练)
- 局限:训练数据的时间截止点(Claude 3.5 的知识截止在 2024 年)
上下文信息(动态):当前对话窗口内的所有信息
- 示例:你刚说的话、你提供的代码、工具调用的结果
- 特点:每次请求都会变化
- 局限:受窗口大小限制(200K tokens)
Lost in the Middle:上下文的位置效应#
研究发现,LLM 对上下文中不同位置的信息关注度是不同的:
上下文位置对关注度的影响:
- 开头:高关注度(系统提示、重要规则)
- 中间:低关注度(Lost in the Middle 现象)
- 结尾:高关注度(最新的用户输入)
研究结论:
- 开头信息:LLM 高度关注(系统提示、重要规则)
- 中间信息:容易被"遗忘"(Lost in the Middle)
- 结尾信息:高度关注(最新的用户输入)
实际影响:
低效的上下文组织:
- 系统提示在开头
- 大量历史对话堆积在中间(容易被忽略)
- 详细的项目说明在中间(容易被忽略)
- 技术栈信息在中间(容易被忽略)
- 用户当前输入在结尾
高效的上下文组织:
- 系统提示在开头
- 关键规则和约束在开头(始终被关注)
- 当前任务的核心信息靠前
- 简化的历史总结
- 用户当前输入在结尾(最高关注)
Claude Code 的优化策略:
- 自动总结历史:长对话自动压缩,避免中间信息堆积
- CLAUDE.md 置顶:项目规则放在开头,确保被关注
- 任务信息靠后:当前任务的具体信息放在接近结尾的位置
200K Tokens ≠ 200K 有效信息#
Claude 3.5 Sonnet 的上下文窗口是 200K tokens,但这不意味着你可以塞进 200K tokens 的信息并期望都被有效利用。
Token 使用量与有效性的关系:
- 最佳区间:30K - 80K tokens(效果最好)
- 衰减开始:100K tokens(开始下降)
- 明显下降:150K+ tokens(效果明显变差)
为什么会有效性下降?
- 计算成本:Token 越多,推理时间越长,注意力分散
- 信息密度:大量信息中,相关信息的密度下降
- Lost in the Middle:中间大量信息被忽略
- 噪声干扰:无关信息越多,准确率越低
实际案例:
场景:修复一个登录 bug
方案 A(5K tokens,高效):
- 错误信息(500 tokens)
- 相关代码(2K tokens)
- 技术栈说明(500 tokens)
- 用户需求(1K tokens)
有效性:95%
方案 B(150K tokens,低效):
- 错误信息(500 tokens)
- 整个项目的所有代码(120K tokens) ← 大量噪声
- 完整的 git 历史(20K tokens) ← 无关信息
- 所有依赖包的文档(8K tokens) ← 干扰判断
- 用户需求(1K tokens)
有效性:40%最佳实践:
- 精准选择:只包含与任务直接相关的文件
- 分块处理:将大任务拆分为多个小任务
- 动态加载:按需读取文件,而不是一次性全部加载
- 智能过滤:使用 Glob/Grep 精确定位,而不是全项目搜索
Token 成本权衡:质量 vs 效率#
在实际使用中,我们需要在输出质量和 Token 成本之间找到平衡点。
成本计算(以 Claude 3.5 Sonnet 为例):
API 定价(2024 年 11 月):
- 输入:$3 / 1M tokens
- 输出:$15 / 1M tokens
实际案例:
任务:修复一个登录 bug
方案 A(精简上下文):
- 输入:5K tokens = $0.015
- 输出:2K tokens = $0.030
- 总成本:$0.045
- 成功率:95%
方案 B(完整上下文):
- 输入:150K tokens = $0.45
- 输出:5K tokens = $0.075
- 总成本:$0.525
- 成功率:40%(因为信息过载)
结论:方案 A 成本低 11 倍,且质量更高成本优化策略:
| 策略 | Token 节省 | 质量影响 | 适用场景 |
|---|---|---|---|
| 精准文件选择 | 70-90% | 无影响 | 所有场景 |
| 总结历史对话 | 50-70% | 微影响 | 长对话 |
| 分块处理任务 | 60-80% | 无影响 | 大型项目 |
| 使用 Subagent | 40-60% | 正向提升 | 复杂任务 |
| 缓存常用上下文 | 20-40% | 无影响 | 重复任务 |
实战建议:
避免全项目扫描:
❌ 低效:Read src/**/* ✅ 高效:Grep "authentication" → Read 相关文件使用增量上下文:
第一轮:提供简要信息 → AI 生成初步方案 第二轮:根据 AI 反馈 → 补充具体信息 第三轮:细化实现善用工具过滤:
Glob:快速定位文件模式 Grep:精确搜索关键词 Read:只读取必要部分合理使用 Plan Mode:
Plan Mode:探索阶段,成本稍高,但避免返工 执行模式:实现阶段,精准上下文,成本更低
核心要点总结#
通过理解上下文的本质,我们可以得出以下关键认知:
- LLM 没有真正的记忆:所有信息都基于当前上下文窗口
- 位置很重要:开头和结尾的信息最受关注,中间容易被忽略
- 多≠好:200K tokens 的容量不代表塞满就有效
- 成本与质量并非正相关:精简的高质量上下文往往更好
这些认知会直接影响我们如何构建和优化上下文,接下来我们会深入探讨具体的优化维度。
上下文工程的五个优化维度#
维度 1:数量(Quantity)- 给多少信息?#
问题:信息太少 vs 信息太多
信息太少 ←──── 最佳点 ────→ 信息太多
↓ ↓ ↓
LLM 无法理解 准确完成 Token 超限实际案例对比:
信息太少的例子:
用户:修复这个 bug
AI:什么 bug?请提供更多详细信息适当的信息量:
用户:修复登录 bug
项目:Next.js + Prisma + PostgreSQL
错误:TypeError: Cannot read 'id' of undefined
位置:src/app/api/auth/login/route.ts:42
期望:正常登录返回 JWT token信息过载的例子:
用户:修复登录 bug
[粘贴整个项目的 10 万行代码]
[包含完整的 git 历史记录]
[包含所有依赖包的源码]优化策略:
- 最小必要原则:只包含必要信息
- 分层提供:先摘要,后细节
- 按需扩展:根据 AI 的反馈补充信息
维度 2:质量(Quality)- 信息的准确性#
高质量上下文的特征:
- 准确性:信息真实可靠,无错误
- 具体性:细节明确,避免模糊
- 时效性:信息是最新的
- 完整性:包含必要的关联信息
质量优化实例:
低质量的描述:
用户:网站有问题,帮我看看高质量的描述:
用户:网站在用户登录后出现 500 错误
环境:生产环境
时间:昨天 20:30 开始
影响:所有新用户无法登录
日志:在 auth.service.ts:45 出现 TypeError
变更:昨天部署了 v2.1.0 版本质量检查清单:
- 错误信息是否准确(复制粘贴,而非口头描述)?
- 环境信息是否完整(开发/测试/生产)?
- 复现步骤是否清晰?
- 期望结果是否明确?
- 相关变更是否说明?
维度 3:结构(Structure)- 信息的组织方式#
为什么结构化更好?
我之前也习惯用一段话把需求全说完,后来发现这样效果很差:
无结构的描述:
用户:用户登录功能需要邮箱密码验证还要检查账户状态是否激活如果是管理员要给管理员权限还要记录登录日志...这种方式下,LLM 需要自己去提取和整理关键信息,很容易遗漏细节。
结构化的描述:
# 用户登录功能需求
## 输入验证
- 邮箱格式验证
- 密码强度检查(至少 8 位)
## 业务逻辑
1. 检查账户存在
2. 验证密码
3. 检查账户状态(激活/禁用)
4. 识别用户角色
## 输出
- 普通用户:JWT token + user info
- 管理员:JWT token + user info + admin permissions
## 附加功能
- 记录登录日志(IP、时间、设备)
- 限制登录尝试次数使用结构化描述后,LLM 可以直接识别各个部分的作用,准确率明显提高。
结构化模板:
# 任务概述
[简要描述要解决的问题]
## 背景
[问题的来源和影响]
## 技术栈
[使用的技术和版本]
## 现状
[当前实现情况]
## 需求
[具体要实现的功能]
## 约束
[限制条件和注意事项]
## 期望结果
[成功的标准]维度 4:时序(Temporal)- 信息的时间顺序#
LLM 的"近因偏差"(Recency Bias):
上下文窗口:
┌───────────────────────┐
│ [很早的信息] │ ← LLM 容易忽略
│ [中间的信息] │
│ [最近的信息] │ ← LLM 重点关注
│ [当前输入] │ ← LLM 最关注
└───────────────────────┘Claude Code 的时序优化:
- 系统提示词:总是在开头(定义行为)
- CLAUDE.md:每次对话注入(项目规则)
- 用户输入:最新信息(最重视)
时序优化策略:
# 优化前的上下文(不推荐)
[长篇的背景介绍...]
[详细的历史对话...]
[前面的技术讨论...]
用户:现在帮我实现用户认证
# 优化后的上下文(推荐)
# 当前任务:实现用户认证功能
## 立即需要的信息
- 项目:Next.js + Prisma + PostgreSQL
- 已有:User 模型、基础 API 结构
- 需求:JWT 认证 + 角色权限
## 背景信息
[简要的项目背景...]
用户:现在帮我实现用户认证维度 5:相关性(Relevance)- 信息的相关程度#
相关性判断实例:
任务:修复登录 bug
高相关的信息:
src/auth/auth.service.ts(直接相关)src/auth/login.controller.ts(业务逻辑)- 错误日志(问题现象)
package.json(依赖版本)
无关的信息:
src/utils/formatDate.ts(工具函数)README.md(项目介绍)tests/e2e/(E2E 测试).gitignore(忽略文件)
Claude Code 的相关性过滤机制:
// 智能文件选择流程
1. Glob: 查找相关文件模式
- **/*auth*.*, **/*login*.*
2. Grep: 搜索关键词
- "authentication", "login", "jwt"
3. Read: 只读取关键部分
- 导出的函数和类
- 错误处理逻辑
- 配置项相关性优化技巧:
- 关键词提取:从任务描述中提取关键技术词
- 文件模式匹配:使用 Glob 模式快速筛选
- 内容预览:先读取文件头部和尾部,判断相关性
- 依赖分析:分析文件间的依赖关系
抽象 vs 具体:如何选择上下文的抽象级别#
核心原则:根据任务类型选择#
不是越抽象越好,也不是越具体越好。需要根据任务类型选择合适的抽象级别:
| 任务类型 | 推荐抽象级别 | 原因 |
|---|---|---|
| 架构设计 | 高度抽象 | 需要关注整体结构和模式 |
| 代码实现 | 中等具体 | 平衡设计思路和实现细节 |
| Bug 修复 | 高度具体 | 需要精确的错误信息和代码 |
| 性能优化 | 中等具体 | 需要性能数据和代码实现 |
| 代码审查 | 中等抽象 | 关注设计原则和代码质量 |
实际案例对比#
场景 A:架构设计(需要抽象)
过于具体的描述:
帮我设计用户系统,具体实现:
- User 类要有 id, name, email 字段
- 使用 class User { constructor() {...} }
- 数据库用 MySQL,表名 users...适度抽象的描述:
帮我设计用户系统的架构:
技术背景:
- Node.js + TypeScript 后端
- 10 万用户规模
- 需要支持第三方登录(Google, GitHub)
需求:
- 用户注册登录
- 角色权限管理(普通用户/管理员)
- 账户安全(2FA, 密码重置)场景 B:Bug 修复(需要具体)
过于抽象的描述:
用户登录功能有问题,帮我修复高度具体的描述:
登录功能报错:
错误信息:TypeError: Cannot read property 'id' of undefined at AuthService.validateUser (src/auth/auth.service.ts:42:15)
相关代码:
```typescript
// src/auth/auth.service.ts:40-45
async validateUser(email: string, password: string) {
const user = await this.userRepository.findByEmail(email);
return user.id; // Line 42: user is undefined
}测试用例:
- 输入:valid@example.com / password123
- 期望:返回用户对象
- 实际:user 为 undefined
数据库查询结果:
- 查询:SELECT * FROM users WHERE email = 'valid@example.com'
- 结果:返回空数组
### 最佳实践:分层上下文策略
```markdown
# 阶段 1:理解需求(抽象层)
高层目标:
- 构建电商网站的用户认证系统
- 支持邮箱注册和社交登录
- 实现角色权限管理
业务需求:
- 用户注册/登录/登出
- 管理员后台管理
- 第三方登录集成
# 阶段 2:设计方案(中等层)
技术选型:
- Next.js 14 + TypeScript
- NextAuth.js 处理认证
- Prisma ORM + PostgreSQL
- JWT token + session 混合模式
架构设计:
- API Routes:/api/auth/*
- 数据库模型:User, Account, Session
- 前端组件:LoginForm, RegisterForm, Profile
# 阶段 3:实现代码(具体层)
现有代码:
- src/app/api/auth/[...nextauth]/route.ts
- prisma/schema.prisma
具体实现:
- 修改 User 模型添加 role 字段
- 实现自定义认证逻辑
- 添加权限中间件
# 阶段 4:测试验证(非常具体)
测试用例:
- 测试用户注册流程
- 验证权限控制
- 检查第三方登录
错误处理:
- 数据库连接失败
- 邮箱重复注册
- Token 过期处理相关的 AI 优化知识领域#
上下文工程是 AI 优化的核心技术之一。了解相关的优化领域能帮助我们更全面地理解如何提升 AI 的输出质量。
1. 提示工程(Prompt Engineering)#
定义:通过优化输入提示词来改善 LLM 输出质量的技术。
与上下文工程的关系:
- 上下文工程:管理整个上下文窗口的信息(包括历史对话、文件内容、工具调用等)
- 提示工程:优化具体的输入提示(如何表达需求、提供示例、引导思路)
核心技巧示例:
Zero-shot:直接描述任务
"帮我实现用户认证"
Few-shot:提供示例
"参考这个登录实现,帮我实现注册功能"
Chain-of-Thought:分步思考
"先分析需求,再设计方案,最后实现代码"详细的 Prompt Engineering 技巧和模式,我们会在后续的攻略 1.4 中深入讲解。
2. RAG(检索增强生成)#
Claude Code 的应用:
工作流程:
1. 检索:Glob + Grep 查找相关代码
2. 增强:将检索结果加入上下文
3. 生成:基于增强的上下文生成回复优势:
- 动态获取最新信息(不受训练数据时间限制)
- 精准定位相关内容(避免全文扫描)
- 降低 Token 消耗(按需检索)
3. In-Context Learning(上下文学习)#
原理:LLM 可以在上下文中"学习"模式,无需重新训练。
实际应用:
// 提供代码风格示例,AI 会自动遵循
export const getUserById = async (id: string): Promise<User | null> => {
try {
return await userRepository.findById(id);
} catch (error) {
throw new Error(`Failed to get user: ${error}`);
}
};
// AI 会按相同风格实现新函数
export const createUser = async (data: CreateUserDto): Promise<User> => {
// 自动遵循相同的错误处理和返回类型
};4. Embedding 和向量搜索#
应用场景:
- 语义相似度搜索("找所有认证相关的代码")
- 代码重复检测
- 智能代码推荐
Claude Code 的应用:
- Skills 的语义匹配触发
- Explore Agent 的智能文件选择
5. Fine-tuning vs Prompt Engineering#
| 维度 | Fine-tuning | Prompt Engineering |
|---|---|---|
| 成本 | 高(训练成本) | 低(优化输入) |
| 灵活性 | 低(需重新训练) | 高(实时调整) |
| 适用场景 | 固定任务 | 多变化任务 |
Claude Code 的选择:采用 Prompt Engineering 而非 Fine-tuning,因为:
- 开发任务灵活多变
- 需要实时调整策略
- 成本控制考虑
实战应用:优化 Claude Code 的上下文策略#
策略 1:提供分层上下文#
# 背景(抽象层)
开发电商网站,需要用户认证系统,支持邮箱注册和社交登录。
# 技术栈(中等层)
- Next.js 14 + TypeScript + Tailwind CSS
- Prisma ORM + PostgreSQL
- NextAuth.js 处理认证
- 部署在 Vercel
# 具体需求(具体层)
1. 用户注册:邮箱验证 + 密码强度检查
2. 用户登录:JWT token + 记住登录状态
3. 社交登录:Google OAuth + GitHub OAuth
4. 权限管理:普通用户/管理员/超级管理员
# 现有代码(具体层)
- User 实体:src/lib/prisma.ts
- 认证配置:src/pages/api/auth/[...nextauth].ts
- 前端组件:src/components/auth/策略 2:使用 CLAUDE.md 预设上下文#
# CLAUDE.md
## 项目上下文
### 技术栈
- Next.js 14 + TypeScript
- Tailwind CSS + Headless UI
- Prisma ORM + PostgreSQL
- NextAuth.js + JWT
### 代码风格
- 函数式组件(React)
- camelCase 命名
- TypeScript 严格模式
- ESLint + Prettier
### 项目结构src/ ├── app/ # App Router ├── components/ # React 组件 ├── lib/ # 工具函数 ├── types/ # TypeScript 类型 └── prisma/ # 数据库模型
### 工作流程
1. 先 Plan Mode 设计方案
2. 实现核心功能
3. 添加测试用例
4. 更新文档
### 常见命令
```bash
npm run dev # 开发服务器
npm run build # 生产构建
npm run test # 运行测试
npx prisma studio # 数据库管理
### 策略 3:善用工具提供具体上下文
**❌ 低效方式:**
```text
用户:登录报错了
用户:[手动复制粘贴错误日志]
用户:[手动复制粘贴相关代码]✅ 高效方式:
用户:登录报错,错误日志在 logs/error.log,相关代码在 src/auth/
Claude Code:
- [自动读取] logs/error.log
- [自动读取] src/auth/login.ts
- [自动读取] src/auth/auth.service.ts
- [分析] 发现是数据库连接问题
- [解决] 更新数据库配置策略 4:动态上下文管理#
智能上下文压缩:
// 长对话自动总结
if (conversationLength > 50) {
await summarizeConversation(); // 压缩历史对话
}
// 相关文件动态加载
if (taskRelatesTo("auth")) {
await loadRelevantFiles(["src/auth/*", "tests/auth/*"]);
}
// 无关信息自动过滤
filteredContext = removeIrrelevantInfo(context, currentTask);上下文优先级排序:
优先级 1:当前任务相关文件
优先级 2:最近修改的文件
优先级 3:用户明确提到的文件
优先级 4:项目配置文件
优先级 5:通用工具函数上下文工程的质量评估#
评估维度#
完整性评分:
- 任务背景是否清晰?
- 技术栈信息是否完整?
- 具体需求是否明确?
- 约束条件是否说明?
相关性评分:
- 是否包含必要的错误信息?
- 是否提供相关代码?
- 是否排除无关信息?
结构性评分:
- 信息组织是否清晰?
- 是否使用合适的格式?
- 逻辑层次是否合理?
自动化检查工具#
// 上下文质量检查函数
function validateContext(context: TaskContext): QualityReport {
const issues = [];
// 检查必要信息
if (!context.error && context.type === "bugfix") {
issues.push("Bug 修复任务缺少错误信息");
}
// 检查技术栈
if (!context.techStack) {
issues.push("缺少技术栈信息");
}
// 检查文件相关性
const irrelevantFiles = detectIrrelevantFiles(context.files);
if (irrelevantFiles.length > 0) {
issues.push(`包含无关文件:${irrelevantFiles.join(", ")}`);
}
return {
score: calculateScore(issues),
issues,
suggestions: generateSuggestions(issues),
};
}一些实践心得#
通过这段时间对上下文工程的学习和实践,我对如何更好地使用 Claude Code 有了更深的理解。
一开始我把 LLM 想象成一个真正"理解"我需求的智能助手,但实际上,它更像是一个基于概率和模式匹配的预测引擎。它能给出什么样的答案,完全取决于我提供了什么样的信息。
我现在习惯在描述问题时,按照"背景 - 技术栈 - 具体需求 - 约束条件"这样的结构来组织信息。这样不仅让我自己的思路更清晰,也让 AI 更容易抓住重点。
抽象层次的把握是我踩过最多坑的地方。做架构设计时提供太多细节,Bug 修复时又描述得太抽象,导致 AI 给出的方案总是偏离预期。现在我会根据任务类型来调整描述的具体程度,效果好了很多。
如果你也在使用 Claude Code,建议:
- 建立一套适合自己项目的上下文模板,每次使用时稍作调整即可
- 在项目的 CLAUDE.md 中写清楚技术栈、代码风格、常见命令等信息
- 充分利用 Claude Code 的工具能力,让它自己去读取文件、运行命令,而不是手动复制粘贴
上下文工程听起来是个很技术化的概念,但本质上就是"如何更清楚地表达自己的需求"。这个能力不仅对使用 AI 工具有帮助,在日常的团队协作中同样重要。